package gui;
import java.awt.*;

import javax.imageio.ImageIO;
import javax.imageio.stream.FileImageInputStream;
import javax.swing.*;

import protocol.GameStateTypes;

import chat.CtfChat;
import client.Main;
import client.CtfState;

import response.MoveBody;
import structures.*;

import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;


/**
 * GUI: graphical representation of the CTF maze.
 * 
 * @author WHOMP
 */
public class CtfGui extends JComponent {
	/**
	 * Default for Serializable class
	 */
	private static final long serialVersionUID = -5619959871432679035L;
	/*
	 * Static final variables
	 */
	static final Color HOME1_COLOR = new Color(255, 105, 180);	// hot pink
	static final Color JAIL1_COLOR = new Color(255, 165, 0);		// orange
	static final Color HOME2_COLOR = new Color(30, 144, 255);	// dodger blue
	static final Color JAIL2_COLOR = new Color(75, 0, 130); // purple //new Color(34, 139, 34);		// forest green
	
	static final Color TEAM1_MY_COLOR = new Color(220, 0, 0);		// dark red 139
	static final Color TEAM1_OTHERS_COLOR = new Color(139, 0, 0);		// dark red 139
	static final Color TEAM2_MY_COLOR = new Color(0, 0, 255);		// dark blue 139
	static final Color TEAM2_OTHERS_COLOR = new Color(0, 0, 139);		// dark red 139
	
	/* Image names in images/ directory */
	static final BufferedImage TEAM1_MY_IMAGE = loadImage("red-mushroom");
	static final BufferedImage TEAM1_OTHERS_IMAGE = loadImage("red-mushroom-retro");
	static final BufferedImage TEAM1_MY_HAS_FLAG1_IMAGE = loadImage("red-mushroom-yellow-star");
	static final BufferedImage TEAM1_OTHERS_HAS_FLAG1_IMAGE = loadImage("red-mushroom-retro-yellow-star");
	static final BufferedImage TEAM1_MY_HAS_FLAG2_IMAGE = loadImage("red-mushroom-purple-star");
	static final BufferedImage TEAM1_OTHERS_HAS_FLAG2_IMAGE = loadImage("red-mushroom-retro-purple-star");
	static final BufferedImage TEAM1_MY_HAS_BOMBS_IMAGE = loadImage("green-mushroom");
	static final BufferedImage TEAM1_OTHERS_HAS_BOMBS_IMAGE = loadImage("red-mushroom-retro-green-bomb");
	static final BufferedImage TEAM1_BOMB_IMAGE = loadImage("fire-flower");
	static final BufferedImage TEAM1_FLAG_IMAGE = loadImage("yellow-star");
	static final BufferedImage TEAM1_WALL_IMAGE = loadImage("red-brick");
	
	static final BufferedImage TEAM2_MY_IMAGE = loadImage("blue-mushroom");
	static final BufferedImage TEAM2_OTHERS_IMAGE = loadImage("blue-mushroom-retro");
	static final BufferedImage TEAM2_MY_HAS_FLAG1_IMAGE = loadImage("blue-mushroom-yellow-star");
	static final BufferedImage TEAM2_OTHERS_HAS_FLAG1_IMAGE = loadImage("blue-mushroom-retro-yellow-star");
	static final BufferedImage TEAM2_MY_HAS_FLAG2_IMAGE = loadImage("blue-mushroom-purple-star");
	static final BufferedImage TEAM2_OTHERS_HAS_FLAG2_IMAGE = loadImage("blue-mushroom-retro-purple-star");
	static final BufferedImage TEAM2_MY_HAS_BOMBS_IMAGE = loadImage("green-mushroom");
	static final BufferedImage TEAM2_OTHERS_HAS_BOMBS_IMAGE = loadImage("blue-mushroom-retro-green-bomb");
	static final BufferedImage TEAM2_BOMB_IMAGE = loadImage("ice-flower");
	static final BufferedImage TEAM2_FLAG_IMAGE = loadImage("purple-star");
	static final BufferedImage TEAM2_WALL_IMAGE = loadImage("blue-brick");

	static final BufferedImage GRASS_IMAGE = loadImage("grass3");
	static final BufferedImage TEAM1_WAIT_IMAGE = loadImage("red-wait");
	static final BufferedImage TEAM2_WAIT_IMAGE = loadImage("blue-wait");
	static final BufferedImage TEAM1_WINS_IMAGE = loadImage("red-wins");
	static final BufferedImage TEAM2_WINS_IMAGE = loadImage("blue-wins");
	static final BufferedImage TEAM1_GAME_OVER_IMAGE = loadImage("red-game-over");
	static final BufferedImage TEAM2_GAME_OVER_IMAGE = loadImage("blue-game-over");
	
	public static final int CELL_LENGTH = 50; // unit height and width
	
	// GUI Options
	static final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
	public static final int WINDOW_WIDTH = (800/CELL_LENGTH) * CELL_LENGTH;	// screenSize.width
	public static final int WINDOW_HEIGHT = (600/CELL_LENGTH) * CELL_LENGTH;	// screenSize.height
	
	/*
	 * Instance variables
	 */
	static Maze maze;
	static JFrame frame;
	static boolean isInitialized = false;
	static boolean drawPlayer = false;
	private static CtfGui gui;
	boolean isFirstStart = true;
	public static CtfChat chat = new CtfChat();
	
	/*
	 * Constructor
	 * 		Initializes JFrame window.
	 */
	public CtfGui( String title, JFrame jf ) { 
		maze = Maze.getInstance();
		//frame = new JFrame( title );
		frame = jf;
		setFrameOptions();
		
		// show
		frame.setVisible(true);
		
		// assign to private representation
		gui = this;
		
		
		
		// CHAT
		Panel panel = new Panel();
		
		panel.setBackground(Color.gray);
        panel.setLayout(new FlowLayout());

        TextField tf = new TextField("", 95) {

        	@Override
            public boolean keyDown(Event evt, int key) {
                if (key == KeyEvent.VK_ENTER) {
                	//CtfChat.addMsg(getText());
                	Main.sendChatMsg(getText());
                	setText("");
                    return true;
                } else {
                	//return true;
                	return super.keyUp(evt, key);
                }
            }

        };
        
       this.addMouseListener( new CtfMouseListener() );
        panel.add(tf);
        panel.setSize( WINDOW_WIDTH, 75);
        panel.setLocation( 0, WINDOW_HEIGHT -75 );
        this.add(panel);
        panel.setVisible(true);
		frame.repaint();
		panel.repaint();
	}
	
	public static CtfGui getInstance() {
		// if not initialized, will be null
		return gui;
	}
	
	/**
	 * Set frame options.
	 */
	public void setFrameOptions() {
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add(this);
		
		//this.setPreferredSize( new Dimension( maze.getNumCols() * CELL_LENGTH, maze.getNumRows() * CELL_LENGTH) );
		
		// scrollbars as needed
		JScrollPane scrollBars = new JScrollPane( this );
		frame.add( scrollBars );
		
		// focus listener
		// Goes back to JavaClient.main(), kills component
//		CtfFocusListener fl = new CtfFocusListener();
//		frame.addFocusListener(fl);
		
		// key listener
		CtfKeyListener kl = new CtfKeyListener();
		this.addKeyListener(kl);
		
		// frame size
		frame.setSize( WINDOW_WIDTH, WINDOW_HEIGHT );
	}
	
	
	public void closeGui() {
		System.exit(0);
	}
	
	/**
	 * Paints 
	 */
	public void paint(Graphics g) {
		ArrayList<Cell> cells = maze.getCellsArrayList();
		
	    for ( Cell cell : cells ) {
	    	
	    	// set color
	    	if ( cell.isFree() )
	    		g.setColor( Color.WHITE );
	    	else if ( cell.isWall() ) 
	    		g.setColor( Color.BLACK );
	    	else if ( cell.isHome1() ) {
	    		g.setColor( HOME1_COLOR );	
	    	}else if ( cell.isHome2() )
	    		g.setColor( HOME2_COLOR ); 	
	    	else if ( cell.isJail1() )
	    		g.setColor( JAIL1_COLOR );	
	    	else if ( cell.isJail2() ) {
	    		g.setColor( JAIL2_COLOR );	
	    	}

	    	// fill with colored rectangles
	    	
	    	// make player in center
	    	int x = Position.getCenteredGraphicalPosX( cell.getPosition()); 
	    	int y = Position.getCenteredGraphicalPosY( cell.getPosition()); 
	    				
	    	
	    	// up bound
	    	if ( y > (WINDOW_HEIGHT) || y < 0 || x > WINDOW_WIDTH || x < 0  ) 
	    		continue;
	    	
	    	
	    	// draw grass
	    	paintImage(g, GRASS_IMAGE, x, y);
	    	
	    	// paint wall image
	    	if ( cell.isWall() && cell.belongsToTeam1() ) 
	    		paintImage(g, TEAM1_WALL_IMAGE, x, y);
	    	else if ( cell.isWall() && cell.belongsToTeam2() ) 
	    		paintImage(g, TEAM2_WALL_IMAGE, x, y);
	    	else if ( cell.isHome() || cell.isJail() )
	    		g.fillRect( x, y, CELL_LENGTH, CELL_LENGTH );
	    	
	    	// draw an image
	    	/*
	    	paintImage(g, TEAM1_MY_IMAGE, 2, 2);
	    	paintImage(g, TEAM1_OTHERS_IMAGE, 2, 3);
	    	paintImage(g, TEAM1_MY_HAS_FLAG1_IMAGE, 3, 2);
	    	paintImage(g, TEAM1_MY_HAS_FLAG2_IMAGE, 3, 5);
	    	paintImage(g, TEAM1_OTHERS_HAS_FLAG1_IMAGE, 3, 3);
	    	paintImage(g, TEAM1_OTHERS_HAS_FLAG2_IMAGE, 3, 4);
	    	paintImage(g, TEAM1_MY_HAS_BOMBS_IMAGE, 2, 4);
	    	paintImage(g, TEAM1_OTHERS_HAS_BOMBS_IMAGE, 2, 5);
	    	paintImage(g, TEAM1_BOMB_IMAGE, 10, 10);
	    	paintImage(g, TEAM1_FLAG_IMAGE, 11, 10);
	    	paintImage(g, TEAM2_BOMB_IMAGE, 10, 11);
	    	paintImage(g, TEAM2_FLAG_IMAGE, 11, 11);
	    	paintImage(g, TEAM2_MY_IMAGE, 18, 17);
	    	paintImage(g, TEAM2_OTHERS_IMAGE, 18, 18);
	    	paintImage(g, TEAM2_MY_HAS_FLAG1_IMAGE, 18, 16);
	    	paintImage(g, TEAM2_MY_HAS_FLAG2_IMAGE, 19, 19);
	    	paintImage(g, TEAM2_OTHERS_HAS_FLAG1_IMAGE, 19, 17);
	    	paintImage(g, TEAM2_OTHERS_HAS_FLAG2_IMAGE, 19, 18);
	    	paintImage(g, TEAM2_MY_HAS_BOMBS_IMAGE, 19, 16);
	    	paintImage(g, TEAM2_OTHERS_HAS_BOMBS_IMAGE, 18, 19);
	    	*/
	    	
	    	
	    	// maze has been initialized
	    	isInitialized = true;	    	
	    } // end paint map
	    

    	// MY TEAM
	    int myTeam = CtfState.getMyPlayer().getTeam();
	    
    	// other players pass Position.getGraphicalPosX(row, col);
    	//Paint all players
    	Iterator<Player> iter = CtfState.getPlayerList().values().iterator();
    	while ( iter.hasNext() ) {
    		Player curPlayer = iter.next();
    		if ( curPlayer.getId() == CtfState.getMyPlayer().getId() )
    			continue;

    		//TODO
    		int x = Position.getCenteredGraphicalPosX( curPlayer.getPos() );
    		int y = Position.getCenteredGraphicalPosY( curPlayer.getPos() ); 
    		
    		// paint 
    		if ( y > (WINDOW_HEIGHT) || y < 0 || x > WINDOW_WIDTH || x < 0  ) 
	    		continue;
    		
    		System.out.println( " GUI: " + curPlayer.getId() + " " + curPlayer.getTeam());
    		
    		if ( curPlayer.getId() < 0 ) {
    			int objId = curPlayer.getId();
    			switch( objId ) {
    			case -1:
    				// team 1 flag
    				paintImage(g, TEAM1_FLAG_IMAGE, x, y);	
    				break;
    			case -2:
    				// team 1 bomb
    				paintImage(g, TEAM1_BOMB_IMAGE, x, y);	
    				break;
    			case -3:
    				// team 2 flag
    				paintImage(g, TEAM2_FLAG_IMAGE, x, y);	
    				break;
    			case -4:
    				// team 2 bomb
    				paintImage(g, TEAM2_BOMB_IMAGE, x, y);	
    				break;
    			}
    				
    		}
    		
    		else if ( curPlayer.getTeam() == 1 ) 
    				paintImage(g, TEAM1_OTHERS_IMAGE, x, y);		
    		else
    			paintImage(g, TEAM2_OTHERS_IMAGE, x, y);
    	}
    	
	    // paint my player
    	if ( CtfState.getMyPlayer() != null ) {
    		if ( myTeam == 1 )
    			paintImage(g, TEAM1_MY_IMAGE, WINDOW_WIDTH/2, WINDOW_HEIGHT/2);
    		else
    			paintImage(g, TEAM2_MY_IMAGE, WINDOW_WIDTH/2, WINDOW_HEIGHT/2);
    	}
    	
    	
    	
    	/* GAME STATE SCREENS */
    	
    	if ( CtfState.getGameState() == GameStateTypes.NOT_YET_STARTED ) {
	    	if ( myTeam == 1 )
	    		g.drawImage(TEAM1_WAIT_IMAGE, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,  null);
			else
				g.drawImage(TEAM2_WAIT_IMAGE, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,  null);
    	} 
    	
    	else if ( CtfState.getGameState() == GameStateTypes.TEAM1_WON ) {
    		
    	}
    	else if ( CtfState.getGameState() == GameStateTypes.NOT_YET_STARTED ) {
    		
    	}
    	else if ( CtfState.getGameState() == GameStateTypes.NOT_YET_STARTED ) {
    		
    	}
    	
    	// add chat box
    	chat.render(g);
    	
    	
	}  // END PAINT
	
	public static void paintImage(Graphics g, BufferedImage img, 
			int imgX, int imgY) {
    	g.drawImage(img, imgX, imgY, CELL_LENGTH, CELL_LENGTH,  null);
	}
	/**
	 * Load a specified image in src/gui/images/ directory
	 * @param filename
	 * @return
	 */
	public static BufferedImage loadImage(String filename) {
		BufferedImage img = null;
		try {
		    img = ImageIO.read( new FileImageInputStream(new File("src/gui/images/"+filename+".png")));
		    return img;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}


class CtfKeyListener implements KeyListener {
	public void keyPressed(KeyEvent ke) {}
	public void keyReleased(KeyEvent arg0) {}
	
	/**
	 * Keyboard events
	 */
	public void keyTyped(KeyEvent ke) {
		char key = ke.getKeyChar();
		switch( key ) {
			case 'w':
				// move up
				MoveBody req = new MoveBody( CtfState.getMyPlayer().getPos().up() );
				Main.sendMove(req);
		
				break;
			case 's':
				// move down
				MoveBody req1 = new MoveBody( CtfState.getMyPlayer().getPos().down() );
				Main.sendMove(req1);
				
				break;
			case 'a':
				// move left
				MoveBody req2 = new MoveBody( CtfState.getMyPlayer().getPos().left() );
				Main.sendMove(req2);
	
				break;
			case 'd':
				// move right
				MoveBody req3 = new MoveBody( CtfState.getMyPlayer().getPos().right() );
				Main.sendMove(req3);
				break;
			case 'e':
				// pickup item
				CtfGui.getInstance().repaint();
				break;
			case 'q':
				// drop item
				CtfGui.getInstance().repaint();
				break;
//			case 'x':
//				// blast
//				break;
			case '0':
				// disconnect
				// popup alert
				int choice = JOptionPane.showConfirmDialog(CtfGui.frame, "Do you want to disconnect from this game?", 
						"CTF Disconnect", JOptionPane.YES_NO_OPTION);
				switch ( choice ) {
					case JOptionPane.YES_OPTION:
						// Send and receive disconnect, then exit
						System.exit(0);	// HACK FOR NOW, need to close sockets, streams
						break;
					case JOptionPane.NO_OPTION:
						break;
				}
				break;
		}
	}
	
}



class CtfFocusListener implements FocusListener {

	public void focusGained(FocusEvent arg0) {
		// TODO Auto-generated method stub
	}

	public void focusLost(FocusEvent arg0) {
		// go back to JavaClient main for console prompt
	}
	
}


class CtfMouseListener implements MouseListener {

	@Override
	public void mouseClicked(MouseEvent arg0) {
		// TODO Auto-generated method stub
		CtfGui.getInstance().grabFocus();
		
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
}
